package com.sw.study.springwork.container;

import com.mysql.jdbc.StringUtils;
import com.sw.study.springwork.annotation.Autowired;
import com.sw.study.springwork.annotation.Service;
import com.sw.study.springwork.annotation.Transactional;
import com.sw.study.springwork.factory.ProxyFactory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by sw on 2020/4/6.
 * 初始化
 */
public class AnnotaionConfigIocContainer {
    private static final String SEPARATOR = File.separator;
    /**
     * 获取到所有的类
     */
    private static Set<Class<?>> totalClass=new LinkedHashSet<>();
    /**
     * 存放bean
     */
    private static ConcurrentHashMap<String,Object> beanConaoners=new ConcurrentHashMap<>();


    /**
     * 构造函数
     * @param scanPackage
     */
    public AnnotaionConfigIocContainer(String scanPackage,Boolean isDe) throws Exception {

        System.out.println("解析项目文件中的注解");
        /**
         * 1。初始化所有的类
         */
        initClass(scanPackage,isDe);
        /**
         * 2。完成Service 注解
         */
        parseAnnotationServiceClass();
        /**
         * 3。完成Autowired注入
         */
        dependencyAutoWired();
        /**
         * 4。处理Transaction 注解
         */
        parseAnnotationTransactionClass();

    }

    /**
     * 初始化所有的包文件
     * @param scanPackage
     * @param isDe
     * @throws IOException
     */
    private void initClass(String scanPackage,Boolean isDe) throws IOException {
        /**
         * 替换包名 转换路径
         */
        String packageDirName = scanPackage.replace('.', '/');
        /**
         * 得到文件全路径
         */
        Enumeration<URL> dirs=Thread.currentThread().getContextClassLoader().getResources(packageDirName);
        while (dirs.hasMoreElements()){
            URL url=dirs.nextElement();
            /**
             * 名称 尝试获取
             */
            String protocol=url.getProtocol();
            System.out.println(url.getProtocol());
            if("file".equals(protocol)){
                String filePath=URLDecoder.decode(url.getFile(),"utf-8");
                findClassFromPackage(scanPackage, filePath,isDe,totalClass);
            }
        }
    }

    /**
     * 找到所有的类
     * @param packageName
     * @param packagePath
     * @param isDe
     * @param classes
     */
    private void findClassFromPackage(String packageName,String packagePath,Boolean isDe,Set<Class<?>> classes){
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 如果是文件，递归循环，拿到以.class结尾的文件
            public boolean accept(File file) {
                return (isDe && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findClassFromPackage(packageName + "." + file.getName(), file.getAbsolutePath(), isDe, classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    classes.add(Class.forName(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从解析的class中获取Service注解信息
     * @return
     */
    public ConcurrentHashMap<String,Object> parseAnnotationServiceClass() throws IllegalAccessException, InstantiationException {
        for (Class<?> clazz : totalClass) {
            Service annotation = clazz.getAnnotation(Service.class);
            if (annotation != null) {
                System.out.println(clazz.getSimpleName());
                Object obj = clazz.newInstance();
                //key为接口类全限定类名称
                if (clazz.getInterfaces() != null && clazz.getInterfaces().length > 0) {
                    String facesName = clazz.getInterfaces()[0].getName();
                    beanConaoners.put(facesName, obj);
                }
                //key为类名首字母小写
                if (StringUtils.isNullOrEmpty(annotation.value())) {
                    String beanId = toLowerCaseFirstOne(clazz.getSimpleName());
                    beanConaoners.put(beanId, obj);
                } else {
                    beanConaoners.put(annotation.value(), obj);
                }
                continue;
            }
        }
        return beanConaoners;
    }

    /**
     * 完成autowired 注入
     * @throws Exception
     */
    public void dependencyAutoWired() throws Exception {
        /**
         * 循环实例化的bean
         *
         */
        for (Map.Entry <String,Object> entry:beanConaoners.entrySet()){
           Object obj= entry.getValue();
            Field [] fields=obj.getClass().getDeclaredFields();
            for(Field f:fields){
                Autowired autowired =f.getAnnotation(Autowired.class);
                if(null!=autowired){
                    /**
                     * 拿到属性名
                     */
                    String fieldName = f.getName();
                    //根据属性名拿
                    Object bean = beanConaoners.get(fieldName);
                    if (bean == null) {
                        /**
                         * 根据属性对应的类的名再去拿
                         */
                        bean = beanConaoners.get(f.getType().getName());
                    }
                    if(null!=bean){
                        /**
                         * 开启属性强制操作
                         */
                        f.setAccessible(true);
                        //调用set方法放入属性
                        f.set(obj,bean);
                    }

                }
            }

        }

    }

    /**
     * 解析 transaction class
     * 和service 一样
     */
    public void parseAnnotationTransactionClass(){
        for(Map.Entry<String,Object> entry:beanConaoners.entrySet()){
            Object obj=entry.getValue();
            if(obj.getClass().getSimpleName().equals("AccountServiceImpl")){
                System.out.println("q2123");
            }
            //获取类注解上的 Transactional
            Transactional transactional=obj.getClass().getAnnotation(Transactional.class);
            /**
             * 得到所有注解
             */
            Method[] methods = obj.getClass().getDeclaredMethods();
            /**
             * 判断方法是否单独加了Transactional
             */
            boolean isTranMethods = Arrays.stream(methods).anyMatch(y -> y.isAnnotationPresent(Transactional.class));
            /**
             * 二者满足一个
             */
            if (transactional != null || isTranMethods) {
                /**
                 * 调用老师写的代理工厂（暂时）
                 */
                ProxyFactory proxyFactory = (ProxyFactory) beanConaoners.get(toLowerCaseFirstOne(ProxyFactory.class.getSimpleName()));
                //判断是否实现接口
                if (obj.getClass().getInterfaces().length > 0) {
                    obj = proxyFactory.getJdkProxy(entry.getValue());
                }else {  //非实现接口的代理类，使用cglib
                    obj = proxyFactory.getCglibProxy(obj);
                }
                beanConaoners.put(entry.getKey(), obj);
            }

        }
    }


    /**
     * 驼峰命名
     * @param s
     * @return
     */
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    public static Object getClass(String classId){
        return beanConaoners.get(classId);
    }


}